#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include <ruby.h>
#include "zip_conf.h"
#include "zip_utils.h"
#include "zip_rb_ext.h"

struct config
{
    VALUE rb_config_;
};

static inline VALUE
_rb_get_config_value(VALUE rb_config, const char *key);

config_t *
conf_create(const char *config_file_path)
{
    config_t *config = malloc(sizeof(config_t));
    if (config)
    {
        VALUE klass = rb_path2class("Zipper::Config");
        VALUE file_path = rb_str_new2(config_file_path);
        VALUE rb_config = rb_funcall(klass, 
                                     rb_intern("parse_config"), 
                                     1, 
                                     file_path);
        if (rb_config == Qnil)
        {
            free(config);
            return NULL;
        }
        config->rb_config_ = rb_config;
        rb_gc_register_address(&config->rb_config_);
    }

    return config;
}

void 
conf_destroy(config_t *config)
{
    rb_gc_unregister_address(&config->rb_config_);
    free(config);
}

int
conf_get_string(const config_t *config, const char *key, char *value)
{
    VALUE arg_value = _rb_get_config_value(config->rb_config_, key);
    if (arg_value != Qnil && TYPE(arg_value) == T_STRING)
    {
        strcpy(value, RSTRING(arg_value)->ptr);
        return 0;
    }
    return 1;
}

int
conf_get_integer(const config_t *config, const char *key, int *value)
{
    VALUE arg_value = _rb_get_config_value(config->rb_config_, key);
    if (arg_value != Qnil && TYPE(arg_value) == T_FIXNUM)
    {
        *value = FIX2INT(arg_value);
        return 0;
    }
    return 1;
}

int
conf_get_float(const config_t *config, const char *key, double *value)
{
    VALUE arg_value = _rb_get_config_value(config->rb_config_, key);
    if (arg_value != Qnil && TYPE(arg_value) == T_FLOAT)
    {
        *value = NUM2DBL(arg_value);
        return 0;
    }
    return 1;
}

int
conf_get_sub_config(const config_t *config, 
                    const char *key, 
                    config_t **value)
{
    VALUE arg_value = _rb_get_config_value(config->rb_config_, key);
    if (arg_value != Qnil)
    {
        config_t *config = malloc(sizeof(config_t));
        rb_gc_register_address(&arg_value);
        config->rb_config_ = arg_value;
        *value = config;
        return 0;
    }
    return 1;
}


static inline VALUE
_rb_get_config_value(VALUE rb_config, const char *key)
{
    VALUE arg_name = rb_str_new2(key);
    return rb_funcall(rb_config, 
                      rb_intern("get_value"),
                      1, 
                      arg_name);
}

int
conf_get_path(const config_t *config, const char *key, char *path)
{
    VALUE arg_name = rb_str_new2(key);
    VALUE rb_path = rb_funcall(config->rb_config_, 
                               rb_intern("get_path"),
                               1, 
                               arg_name);
    if (rb_path)
    {
        strcpy(path, RSTRING(rb_path)->ptr);
        return 0;
    }
    return 1;
}

config_t *
conf_get_named_conf(const config_t *config)
{
    config_t *named_config = NULL;
    int ret = conf_get_sub_config(config, "named_config", &named_config);
    ASSERT(ret == 0, "named config should included in configure module");
    return named_config;
}


